package cn.itcast.ssh.config;

import javax.servlet.FilterRegistration.Dynamic;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;

import org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
/**
 * 实现了Servlet3.0规范的Servlet容器支持在容器启动时通过使用编码的方式来做初始化工作,而这个功能的实现是通过Servlet3.0规范当中的ServletContainerInitializer接口实现的
 * 第三方组件想要实现这个功能必须要在对应的Jar包的META-INF/services下有一个名称为javax.servlet.ServletContainerInitializer的文件,在该文件当中指定接口实现类
 * Spring框架的Web组件当中提供了ServletContainerInitializer接口的实现类是SpringServletContainerInitializer
 * 在SpringServletContainerInitializer类上有一个@HandlesTypes,注解的value值为WebApplicationInitializer接口字节码
 * 
 * 启动流程:
 * 	1.Tomcat容器在启动的时候会给每个应用都创建一个ServletContext容器,而每个ServletContext容器都有一个对应的容器生命周期监听器ContextConfig
 * 	2.在创建完ServletContext容器后并进行初始化时将触发ContextConfig的监控事件方法,在方法中将会开始进行ServletContext的配置
 * 	3.在进行配置的时候将会读取应用中各个jar模块并查找所有ServletContainerInitializer接口的实现类,并从这些实现类当中找出配置了@HandlesTypes注解的实现类
 * 	4.而在Servlet3.0规范中规定了该接口的实现类在jar包当中的META-INF/services目录下的javax.servlet.ServletContainerInitializer文件中进行指定
 * 	5.因此当ContextConfig的事件监控方法在给ServletContext进行配置时将会从Spring的Web组件查找到该接口的实现类SpringServletContainerInitializer,并且这个实现类上加了注解@HandlesTypes
 * 	6.然后ContextConfig还将根据该实现类上的@HandlesTypes注解的值即WebApplicationInitializer接口从应用中找到该接口的实现类并将其放入到一个Set集合中
 * 	7.随后将对所有的ServletContainerInitializer接口实现类进行实例化并放入一个集合中,当Tomcat容器开始启动应用的ServletContext容器时,将会遍历所有的ServletContainerInitializer接口实现类并调用实现类当中的onStartup方法,方法参数中的Set集合即为WebApplicationInitializer接口的实现类Set集合
 * 	8.在ServletContainerInitializer接口的实现类的onStartup方法中当中将遍历Set集合并实例化当中非抽象类的类，随后将这些实例化好的WebApplicationInitializer接口实现类对象放入一个List集合中
 * 	9.随后再遍历该List集合,然后调用遍历到的对象的onStartup方法,而这个时候就刚好调用到了我们自己编写的ApplicationInitializer中的onStartup方法
 * 	10.在这个方法中我们给创建一个注解配置型的Spring容器对象,并给这个容器对象指定Spring注解配置类
 * 	11.随后创建一个Spring的监听器并指定构造参数Spring容器为我们刚才创建好的注解配置型Spring容器,随后将这个监听器注册到ServletContext容器中
 * 	12.在ServletContext容器当中添加一个Struts2的过滤器并配置拦截路径,随后将触发Spring监听器的初始化事件方法
 * 	13.在该方法中将会拿到之前构造方法参数传递过来的注解型Spring容器对象,拿到这个对象后进行初始化容器操作,随后注解型Spring容器将会去解析Spring配置类并将Bean对象注入到容器里
 * 	14.然后将会把这个Spring容器对象放入到ServletContext容器中
 * @author Shugui
 *
 */
public class ApplicationInitializer implements WebApplicationInitializer {

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
		rootContext.register(ApplicationConfig.class);
		servletContext.addListener(new ContextLoaderListener(rootContext));
		Dynamic strutsFilter = servletContext.addFilter("struts", new StrutsPrepareAndExecuteFilter());
		strutsFilter.addMappingForUrlPatterns(null, false, "/*");
	}

}
